package com.fjec.gateway.config;

import com.fjec.common.shiro.CommonRealm;
import com.fjec.common.shiro.MySessionListener;
import com.fjec.common.shiro.MySessionManager;
import com.fjec.common.shiro.filter.MyLoginFilter;
import com.fjec.gateway.filter.MyRolesFilter;
import com.google.common.base.Strings;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.time.Duration;
import java.util.*;

@Configuration
public class ShiroConfig {

    /**
     * Filter工厂，设置对应的过滤条件和跳转条件
     *
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, Filter> filterMap = new HashMap<>();
        filterMap.put("myLogin", new MyLoginFilter());
        filterMap.put("myRoles", new MyRolesFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        // 过滤器链定义映射
        Map<String, String> filterChain = new LinkedHashMap<>();
        filterChain.put("/base/api/n/**", "anon");
        filterChain.put("/base/api/y/**", "myLogin");
        filterChain.put("/base/api/log/**", "anon"); // 上线时需要注释掉
        filterChain.put("/user/api/n/**", "anon");
        filterChain.put("/user/api/y/**", "myLogin");
        filterChain.put("/user/api/login/**", "anon");
        filterChain.put("/user/api/logout", "logout");
        filterChain.put("/market/api/n/**", "anon");
        filterChain.put("/market/api/y/**", "myLogin");
        filterChain.put("/paid/api/n/**", "anon");
        filterChain.put("/paid/api/y/**", "myLogin");
        filterChain.put("/activity/api/n/**", "anon");
        filterChain.put("/activity/api/y/**", "myLogin");
        filterChain.put("/im/api/n/**", "anon");
        filterChain.put("/im/api/y/**", "myLogin");
        filterChain.put("/devops/api/n/**", "anon");
        filterChain.put("/devops/api/y/**", "myLogin");
        // 配器shirot认登录累面地址，前后端分离中登录累面跳转应由前端路由控制，后台仅返回json数据, 对应LoginController中unauth请求
        filterChain.put("/test", "anon");
        filterChain.put("/do/not/forget/me", "anon");
        filterChain.put("/do/you/forget/me", "anon");
        filterChain.put("/un_auth", "anon");
        filterChain.put("/actuator/**", "anon");
        filterChain.put("/**", "myRoles[inner]");
        shiroFilterFactoryBean.setLoginUrl("/un_auth");
        // 未授权界面, 对应LoginController中 unauthorized 请求
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChain);
        return shiroFilterFactoryBean;
    }

    /**
     * 权限管理，配置主要是Realm的管理认证
     *
     * @param sessionManager
     * @param shiroCacheManager
     * @return
     */
    @Bean
    public SecurityManager securityManager(SessionManager sessionManager,
                                           CacheManager shiroCacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//        securityManager.setRealms(realms);
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager);
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(shiroCacheManager);
        return securityManager;
    }


    @Value("${shiro-redis.hostAndPort}")
    private String hostAndPort;

    @Value("${shiro-redis.timeout}")
    private Duration timeout;

    @Value("${shiro-redis.password}")
    private String password;

    @Value("${shiro-redis.sessionTimeout}")
    private int sessionTimeout;

    @Value("${shiro-redis.database}")
    private int database;

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis, 使用的是shiro-redis开源插件
     *
     * @param sessionIdGenerator
     * @return
     */
    @Bean
    public RedisSessionDAO redisSessionDAO(SessionIdGenerator sessionIdGenerator) {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setSessionIdGenerator(sessionIdGenerator);
        // redis超时时间
        redisSessionDAO.setExpire(sessionTimeout);
        return redisSessionDAO;
    }

    /**
     * Session ID 生成器
     *
     * @return
     */
    @Bean
    public SessionIdGenerator sessionIdGenerator() {
        return new JavaUuidSessionIdGenerator();
    }

    /**
     * 自定义sessionManager
     *
     * @param redisSessionDAO
     * @return
     */
    @Bean
    public SessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
        MySessionManager mySessionManager = new MySessionManager();
        mySessionManager.setSessionDAO(redisSessionDAO);
        List<SessionListener> list = new ArrayList<>();
        list.add(new MySessionListener());
        mySessionManager.setSessionListeners(list);
        return mySessionManager;
    }

    /**
     * 配置shiro redisManager, 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean
    RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(hostAndPort);
        redisManager.setTimeout((int) timeout.toMillis());
        redisManager.setDatabase(database);
        if (!Strings.isNullOrEmpty(password)) {
            redisManager.setPassword(password);
        }
        return redisManager;
    }

    /**
     * cacheManager 缓存 redis实现, 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean("shiroCacheManager")
    public RedisCacheManager shiroCacheManager(RedisManager redisManager) {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager);
        // 必须要设置主键名称，shiro-redis 插件用过这个缓存用户信息
        redisCacheManager.setPrincipalIdFieldName("userId");
        return redisCacheManager;
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public SimpleCookie cookie() {
        // cookie的name,对应的默认是 JSESSIONID
        SimpleCookie cookie = new SimpleCookie("SHARE_JSESSIONID");
        cookie.setHttpOnly(true);
        //  path为 / 用于多个系统共享 JSESSIONID
        cookie.setPath("/");
        return cookie;
    }
}
